import { fabric } from 'fabric';

import { getNoAnglePoints } from '@/pages/TemplateEditor/components/Editor/components/TemplateEffect/utils';
import {
  getImageCropped,
  getRotatedPositionByCustomPoint,
  handleRectTopMoving
} from '@/utils/fabricJs';

// 获取裁剪参数，取裁剪框的四个顶点，顺序为：上、右、下、左
export async function getImageCroppedInfo(
  canvas: fabric.Canvas,
  originCanvasSize: number
): Promise<{
  croppedRadioInfo: [number, number, number, number];
  imgSrc: string;
  width: number;
  height: number;
}> {
  const canvasObjects = canvas.getObjects();
  const cropLayerIndex = canvasObjects.findIndex(
    (obj) => obj?.data?.isCropLayer
  );
  const beCroppedLayerIndex = canvasObjects.findIndex(
    (obj) => obj?.data?.isBeCropped
  );
  const cropLayer = canvasObjects[cropLayerIndex];
  const beCroppedLayer = canvasObjects[beCroppedLayerIndex];
  const croppedInfo = getImageCropped(cropLayer, beCroppedLayer);

  const width = cropLayer.getScaledWidth();
  const height = cropLayer.getScaledHeight();

  return {
    croppedRadioInfo: croppedInfo,
    imgSrc: (await getCroppedImageSrc({
      cropPath: cropLayer,
      imgObject: beCroppedLayer,
      canvas,
      croppedInfo
    })) as string,
    width: Math.min(Math.ceil(width), originCanvasSize),
    height: Math.min(Math.ceil(height), originCanvasSize)
  };
}

// 获取截取后的图片base64 src
export const getCroppedImageSrc = async (params: {
  cropPath: fabric.Rect;
  imgObject: fabric.Object;
  canvas: fabric.Canvas;
  croppedInfo: [number, number, number, number];
}): Promise<string> => {
  const { cropPath, imgObject, croppedInfo } = params;
  const topCropped = croppedInfo[0];
  const leftCropped = croppedInfo[3];

  // 截取图片
  return new Promise((resolve) => {
    (imgObject as fabric.Image).cloneAsImage(
      (image: fabric.Image) => {
        resolve(image.getSrc());
      },
      {
        left: (imgObject.getScaledWidth() * leftCropped) / 100,
        top: (imgObject.getScaledHeight() * topCropped) / 100,
        width: cropPath.getScaledWidth(),
        height: cropPath.getScaledHeight()
      }
    ) as fabric.Image;
  });
};

// 进入裁剪状态后的处理，添加 裁剪层clipPath 以及 遮盖层mask
export function addCroppedLayers(
  canvas: fabric.Canvas,
  beCroppedLayer: fabric.Image,
  canvasSize: number,
  productRadio: { width: number; height: number },
  showCropInfo: any
): void {
  const { clipPath, mask } = getCropImageObjects(
    beCroppedLayer,
    canvasSize,
    productRadio,
    showCropInfo
  );

  canvas.add(mask, clipPath);
  canvas.setActiveObject(clipPath);
}

// 根据被裁剪层的信息，增加裁剪层clipPath 以及 遮盖层mask
function getCropImageObjects(
  layer: fabric.Object,
  canvasSize: number,
  productRadio: any,
  showCropInfo: any
): {
  clipPath: fabric.Rect;
  mask: fabric.Rect;
} {
  const { width, height, angle, scaleX, scaleY } = layer;
  const imageWidth = layer.getScaledWidth();
  const imageHeight = layer.getScaledHeight();
  const { center: imageCenterPoint } = getNoAnglePoints(layer);
  const imageCenterTop = imageCenterPoint.y;
  const imageCenterLeft = imageCenterPoint.x;

  const cropLayInfo = getCropLayerInfo({ width, height }, productRadio);

  const minSize = 20;
  const minScaleLimit =
    cropLayInfo.width >= cropLayInfo.height
      ? minSize / cropLayInfo.height
      : minSize / cropLayInfo.width;

  // 裁剪图层
  const clipPath = new fabric.Rect({
    angle,
    width: cropLayInfo.width,
    height: cropLayInfo.height,
    scaleX,
    scaleY,
    top: canvasSize / 2,
    left: canvasSize / 2,
    opacity: 0.01,
    inverted: true,
    absolutePositioned: true,
    fill: 'rgba(255, 255, 255, 0)',
    cornerSize: 10,
    cornerColor: '#00000080',
    borderColor: '#000',
    cornerStrokeColor: '#000',
    transparentCorners: false,
    strokeWidth: 0,
    strokeUniform: true,
    data: {
      // 用于标识裁剪层
      isCropLayer: true
    },
    minScaleLimit: minScaleLimit
  });

  // 设置 Screen 中的裁剪框信息
  showCropInfo(clipPath.getScaledWidth(), clipPath.getScaledHeight());

  // 设置矩形对象的控制点可见性
  clipPath.setControlsVisibility({
    mtr: false, // 旋转控制点可见
    mt: false, // 上中控制点不可见
    mr: false, // 右中控制点不可见
    mb: false, // 下中控制点不可见
    ml: false // 左中控制点不可见
  });

  // 遮盖层图层
  const mask = new fabric.Rect({
    top: canvasSize / 2,
    left: canvasSize / 2,
    width: canvasSize,
    height: canvasSize,
    opacity: 0.5,
    fill: 'rgba(0, 0, 0, 0.75)',
    selectable: false,
    hasControls: false,
    lockMovementX: true,
    lockMovementY: true,
    data: {}
  });

  mask.clipPath = clipPath;

  clipPath.setControlsVisibility({ mtr: false });

  // 图片大小与位置信息
  let imageInfo = {
    imageWidth,
    imageHeight,
    scaleX,
    scaleY,
    imageCenterTop,
    imageCenterLeft,
    angle
  };

  // 事件处理
  clipPath.on('scaling', function (options) {
    handleRectTopScaling(options, this, imageInfo, () => {
      showCropInfo(this.getScaledWidth(), this.getScaledHeight());
    });
  });

  clipPath.on('moving', function (options) {
    handleRectTopMoving(options, this, imageInfo);
  });

  return { clipPath, mask };
}

// 获取图片缩放比例
export function getImageScale(
  imageWidth: number,
  imageHeight: number,
  canvasSize: number
): number {
  const biggerSide = Math.max(imageWidth, imageHeight);
  return canvasSize / biggerSide;
}

// 根据图片的尺寸以及productLayer的比例计算出裁剪框的尺寸
function getCropLayerInfo(
  imageInfo: any,
  productRadioInfo: any
): {
  width: number;
  height: number;
} {
  let width = imageInfo.width;
  let height = imageInfo.height;
  const widthStr = 'width';
  const heightStr = 'height';

  // 返回参数时的处理
  function ceilSize(sizeInfo): {
    width: number;
    height: number;
  } {
    return {
      width: sizeInfo.width,
      height: sizeInfo.height
    };
  }

  // 情况1、如果裁剪框为正方形，则取图片短的一边
  if (productRadioInfo.width === productRadioInfo.height) {
    width = height = Math.min(imageInfo.width, imageInfo.height);

    return ceilSize({
      width,
      height
    });
  }

  // 情况2、如果裁剪框与图片比例相同，则裁剪框长宽与图片相同
  if (
    imageInfo.width / imageInfo.height ===
    productRadioInfo.width / productRadioInfo.height
  ) {
    return ceilSize({
      width,
      height
    });
  }

  // 将长和高放入数组，长的一边放前面
  let sideOrder = [widthStr, heightStr];
  if (imageInfo[widthStr] < imageInfo[heightStr]) {
    sideOrder = [heightStr, widthStr];
  }
  const imageRatio = imageInfo[sideOrder[0]] / imageInfo[sideOrder[1]];
  const productRatio =
    productRadioInfo[sideOrder[0]] / productRadioInfo[sideOrder[1]];

  // 情况3、如果图片 长>=高，裁剪框的尺寸也是 长>=高，并且 长 / 高 【裁剪框】> 长 / 高【图片】，则取图片长的一边
  if (
    imageInfo[sideOrder[0]] >= imageInfo[sideOrder[1]] &&
    productRadioInfo[sideOrder[0]] >= productRadioInfo[sideOrder[1]]
  ) {
    if (productRatio > imageRatio) {
      return ceilSize({
        [sideOrder[0]]: imageInfo[sideOrder[0]],
        [sideOrder[1]]: Math.ceil(imageInfo[sideOrder[0]] / productRatio)
      });
    }
  }

  // 情况4、其他情况，则取图片短的一边，长边通过比例算出
  return ceilSize({
    [sideOrder[1]]: imageInfo[sideOrder[1]],
    [sideOrder[0]]: Math.ceil(
      imageInfo[sideOrder[1]] /
        (productRadioInfo[sideOrder[1]] / productRadioInfo[sideOrder[0]])
    )
  });
}

// 拖拽时触发
const handleRectTopScaling = (
  options: fabric.IEvent<MouseEvent>,
  cropLayer: fabric.Rect,
  imageInfo,
  callback?: () => void
): void => {
  options.e.stopPropagation();
  const {
    imageWidth,
    imageHeight,
    scaleX,
    scaleY,
    imageCenterTop,
    imageCenterLeft,
    angle
  } = imageInfo;
  const maxScaleX = scaleX;
  const maxScaleY = scaleY;

  // 由于开始时裁剪框的大小与图片大小相同，限制scale，避免裁剪框的大小超过图片
  if (cropLayer.scaleX > maxScaleX) {
    cropLayer.set({
      scaleX: maxScaleX
    });
  }
  if (cropLayer.scaleY > maxScaleY) {
    cropLayer.set({
      scaleY: maxScaleY
    });
  }

  // 计算摆正后的图片坐标
  const imagePoints = getNoAnglePoints({
    width: imageWidth,
    height: imageHeight,
    left: imageCenterLeft,
    top: imageCenterTop
  });

  // 限制裁剪框的大小，不能大于被裁剪图片
  const currentWidth = Math.min(imageWidth, cropLayer.getScaledWidth());
  const currentHeight = Math.min(imageHeight, cropLayer.getScaledHeight());

  // 计算裁剪框原点坐标的拖动范围(裁剪范围)，限制裁剪框的大小不能超出被裁剪图片
  const topBound = imagePoints.leftTop.y + currentHeight / 2;
  const bottomBound = imagePoints.rightBottom.y - currentHeight / 2;
  const leftBound = imagePoints.leftTop.x + currentWidth / 2;
  const rightBound = imagePoints.rightBottom.x - currentWidth / 2;

  // 计算裁剪框摆正后的原点坐标，用于比对是否超出裁剪范围
  const { x: rectCenterLeftInBound, y: rectCenterTopInBound } =
    getRotatedPositionByCustomPoint(
      imagePoints.center,
      new fabric.Point(cropLayer.left, cropLayer.top),
      angle
    );

  // 当裁剪框宽度超出被裁剪图片时，限制裁剪框的大小
  if (rectCenterLeftInBound < leftBound || rectCenterLeftInBound > rightBound) {
    // 如果裁剪框没有变化，则复用已保存的值
    if (cropLayer.data.lockScaleX) {
      cropLayer.set({
        scaleX: cropLayer.data.lockScaleX,
        scaleY: cropLayer.data.lockScaleX
      });
    } else {
      let newWidth =
        rectCenterLeftInBound < leftBound
          ? // 如果裁剪框左侧超出，则裁剪框大小需要剪掉左侧超出的范围
            currentWidth / 2 - (imagePoints.leftTop.x - rectCenterLeftInBound)
          : // 如果裁剪框右侧超出，则裁剪框大小需要剪掉右侧超出的范围
            currentWidth / 2 -
            (rectCenterLeftInBound - imagePoints.rightBottom.x);
      const newScaleX = (cropLayer.scaleX * newWidth) / currentWidth;
      // 缩放到裁剪框大小
      cropLayer.set({
        scaleX: newScaleX,
        data: {
          ...cropLayer.data,
          lockScaleX: newScaleX
        }
      });
    }
  } else if (cropLayer.scaleX !== cropLayer.data.lockScaleX) {
    // 如果发生变化，则需要重新计算
    cropLayer.data.lockScaleX = null;
  }

  // 当裁剪的高度超出裁剪范围时，限制裁剪框的大小
  if (rectCenterTopInBound < topBound || rectCenterTopInBound > bottomBound) {
    if (cropLayer.data.lockScaleY) {
      cropLayer.set({
        scaleY: cropLayer.data.lockScaleY,
        scaleX: cropLayer.data.lockScaleY
      });
    } else {
      let newHeight =
        rectCenterTopInBound < topBound
          ? rectCenterTopInBound + currentHeight / 2 - imagePoints.leftTop.y
          : imagePoints.rightBottom.y -
            (rectCenterTopInBound - currentHeight / 2);
      const newScaleY = (cropLayer.scaleY * newHeight) / currentHeight;
      cropLayer.set({
        scaleY: newScaleY,
        data: {
          ...cropLayer.data,
          lockScaleY: newScaleY
        }
      });
    }
  } else if (cropLayer.scaleY !== cropLayer.data.lockScaleY) {
    cropLayer.data.lockScaleY = null;
  }

  callback && callback();

  handleRectTopMoving(options, cropLayer, imageInfo);
};
